<template>
  <el-popover v-model="visible"
              placement="bottom"
              trigger="click"
              :width="popoverWidth"
              popper-class="xdh-tree-select__popover">
    <div :class="{'xdh-tree-select__bd': true, 'xdh-tree': isDashed}">
      <el-scrollbar>
        <el-tree
          :data="data"
          :empty-text="emptyText"
          :node-key="nodeKey"
          :props="props"
          :render-after-expand="renderAfterExpand"
          :load="load"
          :render-content="renderContent"
          :highlight-current="highlightCurrent"
          :default-expand-all="defaultExpandAll"
          :expand-on-click-node="expandOnClickNode"
          :auto-expand-parent="autoExpandParent"
          :default-expanded-keys="checkedKeys"
          :show-checkbox="showCheckbox"
          :check-strictly="checkStrictly"
          :default-checked-keys="checkedKeys"
          :filter-node-method="filterNodeMethod"
          :accordion="accordion"
          :indent="isDashed ? 18 : indent"
          ref="tree"></el-tree>
      </el-scrollbar>
      <select-footer
        :clear="showClearBtn"
        :submit="showOkBtn"
        @on-clear="clearChecked"
        @on-submit="handleSelected">
        <slot name="footer"></slot>
      </select-footer>
    </div>
    <div class="xdh-tree-select__reference" slot="reference" ref="reference">
      <slot :value="values" :nodes="currentCheckedNodes">
        <el-input v-if="showCheckbox" :value="'已选择'+values.length+'项'" readonly
                  suffix-icon="el-icon-caret-bottom" :placeholder="placeholder"></el-input>
        <el-input v-else :value="(values||[]).join(',')" readonly suffix-icon="el-icon-caret-bottom"
                  :placeholder="placeholder"></el-input>
      </slot>
    </div>
  </el-popover>
</template>


<script>
  /**
   * treeSelect树状选择组件
   * @module widgets/xdh-tree-select
   */
  import {Tree} from 'element-ui'
  import {addResizeListener, removeResizeListener} from 'element-ui/lib/utils/resize-event'
  import throttle from 'lodash.throttle'
  import SelectFooter from './select-footer.vue'

  const treeProps = Object.assign({}, Tree.props)

  export default {
    name: 'XdhTreeSelect',
    components: {
      SelectFooter
    },
    /**
     * 参数属性
     * @property {Object} [props = {}] 组件的数据展示配置项（指导组件根据data内的数据结构来显示）
     * @property {String | Function(data, node)} [props.label] 指定节点标签（显示的文字）为节点数据对象中的某个属性值
     * @property {String} [props.children] 指定子树节点对象为（当前层）节点数据中的某个属性值
     * @disabled {Boolean | Function(data, node)} [props.disable] 指定用节点对象中某个属性值（输出布尔值）来定义当前节点选择框是否可选
     * @property {Boolean |Function(data, node)} [props.isLeaf] 根据节点对象中的某个属性值来确定当前节点是否叶子节点（不能再往下展开）
     * @property {array} [data] treeSelect的展示数据。 data = [{label: 'A', children:[{label: 'A1'}, {label: 'A2'}]}]
     * @property {String} [node-key] 每个树节点用来做唯一标示的属性
     * @property {String} [empty-text] 树状列表区域内容为空时的占位文字
     * @property {Boolean} [render-after-expand = true] 是否为首次点开某个树节点后才渲染其子节点, 默认为true
     * @property {FUnction(h, {node, data, store})} [render-content] 树节点的内容区的渲染 Function(h()) 用于自定义节点
     * @property {Boolean} [highlight-current = false] 默认是否高亮当前选中节点
     * @property {Boolean} [default-expand-all = false] 是否默认展开所有节点
     * @property {Boolean} [expand-on-click-node = true] 是否在点击节点的时候展开或者收缩节点， 默认值为 true，如果为 false，则只有点箭头图标的时候才会展开或者收缩节点。
     * @property {Boolean} [auto-expand-parent = true] 展开子节点的时候是否自动展开父节点
     * @property {Boolean} [showCheckbox = false] 节点是否可被选择
     * @property {Boolean} [check-strictly = false] 在显示复选框的情况下，是否严格的遵循父子不互相关联的做法，默认为 false
     * @property {Array} [default-expanded-keys] 默认展开的节点的 key 的数组
     * @property {Array} [default-checked-keys] 默认勾选的节点的 key 的数组
     * @property {Boolean} [accordion = false] 是否使用手风琴模式（每次只有一个同级树节点展开）
     * @propterty {Number} [indent = 18] 子节点缩进距离（默认18px） （PS：EL-UI文档写默认16px，实测18px）
     * @property {Boolean} [isDashed = false] 是否使用带虚线模式的树组件（isDashed为 true的 情况下，indent将被固定位18px）
     * @property {Number} [width] popOver容器组件的宽度
     * @property {Array} [value] 选择组件 进行数据双向绑定的变量
     * @property {Boolean} [showClearBtn = true] 是否显示下方清空按钮
     * @property {Boolean} [showOkBtn = true] 是否显示下方确定按钮
     * @property {String} [placeholder] 输入框的占位文字
     */
    props: {
      ...treeProps,
      isDashed: {
        type: Boolean,
        default: false
      },
      // 重写默认显示checkbox
      showCheckbox: {
        type: Boolean,
        default: true
      },
      width: {
        type: Number
      },
      value: {
        type: [Array, String]
      },
      showClearBtn: {
        type: Boolean,
        default: true
      },
      showOkBtn: {
        type: Boolean,
        default: true
      },
      // 输入框显示选择的项
      placeholder: {
        type: String,
        default: '请选择'
      },
      highlightCurrent: {
        type: Boolean,
        default: true
      }
    },
    watch: {
      data(val) {
        this.$nextTick(() => {
          let nodes = this.$refs.tree.getCheckedNodes()
          if (!this.showCheckbox) { // 单选树
            nodes = this.$refs.tree.getCurrentNode() ? [this.$refs.tree.getCurrentNode()] : []
          }
          this.currentCheckedNodes = nodes
          this.valueToLabel(this.value)
        })
      },
      value(val) {
        this.valueToLabel(val)
      }
    },
    data() {
      return {
        popoverWidth: 'auto',
        visible: false,
        currentCheckedNodes: [],
        values: []
      }
    },
    computed: {
      checkedKeys() {
        return Array.isArray(this.value) ? this.value : [this.value]
      }
    },
    methods: {
      setPopoverWidth() {
        let width = this.width ? this.width : this.$refs.reference.getBoundingClientRect().width - 25
        this.popoverWidth = width
      },
      /**
       * 清空所有选中项, 点击清除按钮是执行
       * @Function clearChecked
       */
      clearChecked() {
        if (this.showCheckbox) {
          this.$refs.tree.setCheckedNodes([])
        } else {
          this.$refs.tree.setCurrentNode({})
        }
      },
      /**
       * 确认选中方法，在点击确认按钮时执行
       * @Function handleSelected
       */
      handleSelected() {
        let nodes = this.$refs.tree.getCheckedNodes()
        if (!this.showCheckbox) { // 单选树
          nodes = this.$refs.tree.getCurrentNode() ? [this.$refs.tree.getCurrentNode()] : []
        }
        this.currentCheckedNodes = nodes
        this.visible = false
        /**
         * 点击确认时派发事件，输出为选中的节点对象数组
         * @event on-selected
         * @param {Array} [nodes] 选中节点的数组
         * @param {Object} [nodes.item] 选中节点数组的完整对象
         */
        this.$emit('on-selected', nodes)
        this.updateInput(nodes)
        if (this.nodeKey) {
          let keys = nodes.map(node => node[this.nodeKey])
          /**
           * 双向绑定input事件（直接用v-model监听）， 输出为选中的字符值数组
           * @event input
           * @param {Array} [keys] 选中节点值得数组
           * @param {String} [keys.item] 节点数据中作为显示label的字符串值
           */
          if (this.showCheckbox) {
            this.$emit('input', keys)
          } else {
            this.$emit('input', keys[0])
          }
        }
      },
      /**
       * 输入框的id 转换成 label
       * @param val Array 输入框的ID
       */
      valueToLabel(val) {
        val = Array.isArray(val) ? val : [val]
        this.values = []
        let cDataMap = d => {
          d.map(res => {
            if (val.includes(res[this.nodeKey])) {
              this.values.push(res[this.props.label])
            }
            cDataMap(res[this.props.children] || [])
          })
        }
        cDataMap(this.data || [])
        this.$nextTick(() => {
          // 选中回填
          if (this.showCheckbox) {
            this.$refs.tree.setCheckedKeys(val)
          } else {
            this.$refs.tree.setCurrentKey(val[0])
          }
        })
      },
      /**
       * 根据树勾选更新输入框数据
       * @param node Array 当前勾选对象数组
       */
      updateInput(node) {
        this.values = []
        node.map(n => {
          this.values.push(n[this.props.label])
        })
      }
    },
    created() {
      this.handleSetPopoverWidth = throttle(this.setPopoverWidth, 150, {leading: false})
      this.valueToLabel(this.value)
    },
    mounted() {
      if (this.value) {
        let nodes = this.$refs.tree.getCheckedNodes()
        if (!this.showCheckbox) { // 单选树
          nodes = this.$refs.tree.getCurrentNode() ? [this.$refs.tree.getCurrentNode()] : []
          this.$refs.tree.setCurrentKey(this.value)
        }
        this.currentCheckedNodes = nodes
      }
      this.setPopoverWidth()
      addResizeListener(this.$refs.reference, this.handleSetPopoverWidth)
    },
    beforeDestroy() {
      removeResizeListener(this.$refs.reference, this.handleSetPopoverWidth)
    }
  }
</script>
